#!/usr/bin/env python2

########### VOLUME_APPLET ###########################################
# Author: Mitchell Nemitz [mitchell.nemitz@gmail.com]               #
# Purpose: A simple tray applet to adjust the Master volume.        #
# Description: Uses python alsa bindings to set and get the volume. #
#####################################################################

import os
import sys
import os.path
import subprocess

try:
    import pygtk
except:
    print "Error: Failed to import module pygtk"
    exit()

try: 
    pygtk.require('2.0')
except:
    print "Error: Failed to set version to GTK+2.0"
    exit()

try:
    import gtk
except:
    print "Error: Failed to import GTK+2.0"
    exit()

name     = 'Volume Control'
version  = '0.6.1'
comments = \
'''Volume Control is a simple tool to adjust your volume levels.
It is written in python and supports multiple audio backends.\n
Written by Mitchell Nemitz [mitchell.nemitz@gmail.com]\n'''

class About(gtk.AboutDialog):
    def __init__(self):
        gtk.AboutDialog.__init__(self)
        self.set_program_name(name)
        self.set_version(version)
        self.set_comments(comments)
    
    def display(self, widget, event):
        self.run()
        self.hide()

class Slider(gtk.VScale):
    def __init__(self, backend):
        gtk.VScale.__init__(self)
        self.set_inverted(True)
        self.set_range(0, 100)
        self.set_increments(1, 10)
        self.set_digits(0)
        self.set_size_request(34, 160)
        self.set_value_pos(gtk.POS_BOTTOM)
        self.set_value(backend.get_volume())
        self.connect('value-changed', self.set_volume, backend)
        
    def set_volume(self, widget, backend):
        backend.set_volume(self.get_value())

class Mute(gtk.CheckButton):
    def __init__(self, backend):
        gtk.CheckButton.__init__(self, label="mute")
        self.connect('toggled', self.toggle_mute, backend)
        
    def toggle_mute(self, widget, backend):
        backend.set_mute(self.get_active())

class Menu(gtk.Menu):
    def __init__(self, about):
        gtk.Menu.__init__(self)
        menu_sets  = gtk.ImageMenuItem(gtk.STOCK_EXECUTE)
        menu_about = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
        menu_quit  = gtk.ImageMenuItem(gtk.STOCK_QUIT)
        
        menu_sets.set_label("_Settings")
        
        self.append(menu_sets)
        self.append(menu_about)
        self.append(menu_quit)
        
        menu_sets.connect( 'button-press-event', self.settings)
        menu_about.connect('button-press-event', about.display)
        menu_quit.connect( 'button-press-event', gtk.main_quit)
        
    def settings(self, widget, data=None):
        global settings
        if settings == "internal":
            gtk.window(gtk.WINDOW_TOPLEVEL).present()
            return 0
        else:
            subprocess.Popen(settings)
        
    def display(self, widget, button, time):
        if button == 3:
            self.show_all()
            self.popup(None, None, None, 3, time)

class Window(gtk.Window):
    def __init__(self, slider, mute, backend):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.set_decorated(False)
        self.set_skip_taskbar_hint(True)
        self.set_skip_pager_hint(True)
        
        vbox = gtk.VBox()
        vbox.set_homogeneous(False)
        vbox.add(slider)
        vbox.add(mute)
        
        frame = gtk.Frame()
        frame.add(vbox)
        
        self.add(frame)
        self.show_all()
        self.hide()
        
    def lost_focus(self, widget, event, slider, mute, backend):
        self.hide()
        
    def display(self, widget, event, slider, mute, backend):
        if self.get_property('visible'):
            self.hide()
        else:
            slider.set_value(backend.get_volume())
            mute.set_active(backend.get_mute())
            
            position = widget.position(self.get_size())
            self.move(position[0], position[1])
            
            self.show_all()
            self.present()

class Applet(gtk.StatusIcon):
    def __init__(self, window, menu, slider, mute, backend):
        gtk.StatusIcon.__init__(self)
        
        if stockicon: 
            self.set_from_stock(gtk.STOCK_MEDIA_RECORD)
        else:
            if os.path.isfile(icon):
                self.set_from_file(icon)
            else:
                print "Error: Icon %s not found." %(icon)
                exit(0)
        
        window.connect( 'focus-out-event', window.lost_focus, slider, mute, backend )
        self.connect(   'activate',        window.display, None, slider, mute, backend)
        self.connect(   'popup-menu',      menu.display)
        self.connect(   'scroll-event',    self.scroll, backend, slider  )
    
    def scroll(self, widget, event, backend, slider):
        if event.direction == gtk.gdk.SCROLL_UP:
            volume = slider.get_value()+interval
            
            if volume > 100: slider.set_value(100)
            else:            slider.set_value(volume)
            
        if event.direction == gtk.gdk.SCROLL_DOWN:
            volume = slider.get_value()-interval
            
            if volume < 0: slider.set_value(0)
            else:          slider.set_value(volume)
    
    def position(self, size):
        screen, position, orientation = self.get_geometry()
        
        width  = screen.get_width()
        height = screen.get_height()
        
        size_x, size_y = size
        
        if orientation == gtk.ORIENTATION_HORIZONTAL:
            x = (position[0] + (position[2] / 2)) - (size_x / 2)
            y = position[1]
        
        if orientation == gtk.ORIENTATION_VERTICAL:
            pass
        
        return (x, y)

def set_backend():
    global backend 
    
    if subsystem == "alsa":
        from VolumeAppletBackends import ALSA
        backend = ALSA()
        
    elif subsystem == "oss":
        from VolumeAppletBackends import OSS # TODO
        backend = OSS()
        
    elif subsystem == "esd":
        from VolumeAppletBackends import ESD # TODO
        backend = ESD()
        
    elif subsystem == "jack":
        from VolumeAppletBackends import JACK # TODO
        backend = JACK()
        
    elif subsystem == "pulse":
        from VolumeAppletBackends import PULSE # TODO
        backend = PULSE()
        
    else:
        print "Error: Failed to import module from VolumeAppletBackends"
        exit()
        
def read_config():
    import ConfigParser
    
    global stockicon
    global icon
    global subsystem
    global interval
    global settings
    
    configfile = os.path.join(os.getenv("HOME"), '.volapp')
    
    if os.path.isfile(configfile):
        parser = ConfigParser.SafeConfigParser()
        parser.read(configfile)
        
        try:
            stockicon = parser.getboolean( 'volume-applet', 'default-icon'  )
            icon      = parser.get(        'volume-applet', 'icon'          )
            subsystem = parser.get(        'volume-applet', 'audio-backend' )
            settings  = parser.get(        'volume-applet', 'settings'      )
            interval  = int(parser.get(    'volume-applet', 'interval'      ))
        except:
            print "Error: Failed to import configuration from ~/.volapp"
            exit()
    
    else:
        try:
            configfile = open(configfile, 'w')
            parser = ConfigParser.SafeConfigParser()
            parser.add_section('volume-applet')
            parser.set('volume-applet', 'default-icon',  'false'      )
            parser.set('volume-applet', 'icon',          '/usr/lib/volume-applet/icons/volume-icon.png')
            parser.set('volume-applet', 'audio-backend', 'alsa'       )
            parser.set('volume-applet', 'interval',      '2'          )
            parser.set('volume-applet', 'settings',      'xfce4-mixer')
            parser.write(configfile)
        except:
            print "Error: Failed to write default configuration to ~/.volapp"
            exit()
            
if __name__ == '__main__':
    read_config()
    set_backend()
    
    about   = About()
    slider  = Slider(backend)
    mute    = Mute(backend)
    menu    = Menu(about)
    window  = Window(slider, mute, backend)
    applet  = Applet(window, menu, slider, mute, backend)
    
    gtk.main()
